#include "common.h"
#include "udpCommunication.h"

#define SOCKET_BUF_SIZE 256 * 1024

static unsigned char heart_packet[4] = {'C' , 'N' , 'X' , 'T'};

UdpCommunication::UdpCommunication()
{
	sockfd = -1;
	memset(&destAddr , 0 , sizeof(destAddr));
	
}


UdpCommunication::~UdpCommunication()
{
	close(sockfd);
}


int UdpCommunication::initServer(unsigned short port)
 {
	struct sockaddr_in servaddr;
	unsigned long sock_buf_size = SOCKET_BUF_SIZE;
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	fcntl(sockfd,F_SETFL, O_NONBLOCK);
	setsockopt( sockfd, SOL_SOCKET, SO_SNDBUF,
                   (char *)&sock_buf_size, sizeof(sock_buf_size) );
	setsockopt( sockfd, SOL_SOCKET, SO_RCVBUF,
                   (char *)&sock_buf_size, sizeof(sock_buf_size) );
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(port);
	if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
	{
		perror("bind error");
		return -1;
	}

	memset(clientAddrTab , 0 , sizeof(clientAddrTab));

 	return 0;
 }


int UdpCommunication::getPacket(void * buffer , int len)
{
	int ret = -1;
	struct sockaddr_in clientAddr;
	unsigned char * p = (unsigned char *)buffer;
	socklen_t src_len = sizeof(struct sockaddr_in);
	memset(&clientAddr , 0 , sizeof(clientAddr));
	ret = recvfrom(sockfd, buffer, len , 0, (struct sockaddr *)&clientAddr, &src_len);
	time_t t = time(NULL);
	if(destAddr.sin_port)
	{
		if(t != lastHeartBeatTime)
		{
			sendto(sockfd, heart_packet, 4, 0, (struct sockaddr *)&destAddr, sizeof(struct sockaddr_in));
			lastHeartBeatTime = t;
		}
	}
	else
	{
		//check heart beat packet
		if (ret == 4)
		{
			if (memcmp(buffer , heart_packet , 4) == 0)
			{
				int i;
				for (i = 0 ; ;)
				{
					if (memcmp(clientAddrTab + i , &clientAddr , sizeof(struct sockaddr_in)) == 0)
					{
						clientHeartBeatTime[i] = t;
						break;
					}
					if (i >= MAX_CLIENT_COUNT - 1)
					{
						int j;
						for (j = 0 ; j < MAX_CLIENT_COUNT; ++j)
						{
							if (clientAddrTab[j].sin_port == 0)
							{
								memcpy(clientAddrTab + j , &clientAddr , sizeof(struct sockaddr_in));
								clientHeartBeatTime[j] = t;
								break;
							}
						}
						break;
					}
					++i;
				}
			}
		}
	}
    return ret;
}


void UdpCommunication::sendPacket(void * buffer , int len)
{
	int r;
	if(destAddr.sin_port)
	{
		r = sendto(sockfd, buffer, len, 0, (struct sockaddr *)&destAddr, sizeof(struct sockaddr_in));
	}
	else
	{
		int j;
		time_t t = time(NULL);
		for (j = 0 ; j < MAX_CLIENT_COUNT; ++j)
		{
			if (clientAddrTab[j].sin_port)
			{
				unsigned dif = (unsigned)(t - clientHeartBeatTime[j]);
				if(dif > MAX_HEART_BEAT_TIME)
				{
					memset(clientAddrTab + j , 0 , sizeof(struct sockaddr_in));
				}
				else
				{
					r = sendto(sockfd, buffer, len, 0, (struct sockaddr *)clientAddrTab + j, 
							   sizeof(struct sockaddr_in));
				}
			}
		}
	}
}



int UdpCommunication::initUdpClient(char *serverIp, unsigned short serverPort)
{
    unsigned long sock_buf_size = SOCKET_BUF_SIZE;
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	fcntl(sockfd,F_SETFL, O_NONBLOCK);
	setsockopt( sockfd, SOL_SOCKET, SO_SNDBUF,
                   (char *)&sock_buf_size, sizeof(sock_buf_size) );
	setsockopt( sockfd, SOL_SOCKET, SO_RCVBUF,
                   (char *)&sock_buf_size, sizeof(sock_buf_size) );

    memset(&destAddr, 0, sizeof(destAddr));
    destAddr.sin_family = AF_INET;
    destAddr.sin_port = htons(serverPort);
    if(inet_aton(serverIp, &destAddr.sin_addr) < 0)
    {
		printf("[%s] is not a valid IPaddress\n", serverIp);
		return -1;
    }

	lastHeartBeatTime = 0;
    return 0;
}


